/*
 * T.Raynal - A.Aviles - F.Diallo
 * XML-Scenarios
 * PPA AL5-2
 * December 2012 
 */
package model.scenario;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.xml.sax.InputSource;

import common.Commons;
import common.Worker;
import common.exception.UnregisteredOperatorException;
import common.interfaces.Callback;
import model.Case;
import model.Expression;
import model.Situation;
import model.Scenario;
import model.expression.DynamicExpression;
import model.expression.LogicalExpression;
import model.expression.QuantifiableExpression;

// TODO: Auto-generated Javadoc
/**
 * Helper class for DOM parsing
 */
public class ScenarioBuilder implements Callable<List<Scenario>>  {
	
	private int currentState =2;
	
	private String _xml;
	
	private Callback<List<Scenario>> _callback;
	

	/**
	 * Instantiates a new scenario builder.
	 *
	 * @param XML file
	 */
	public ScenarioBuilder(String xml)
	{
		_xml = xml;
	}
	
	/**
	 * Builds asynchronously from the target XML file
	 *
	 * @param Callback
	 * @throws Exception
	 */
	public void build(Callback<List<Scenario>> callback) throws Exception
	{		
		_callback = callback;
		Worker<List<Scenario>> worker = new Worker<List<Scenario>>(this);
		worker.doWork();
	}
	

	private  Situation toSituation (Element situation) throws XmlParsingException, UnregisteredOperatorException
	{
		Situation si = new Situation(
				situation.getAttributeValue(Commons.NAME)
				);
		
		currentState = 2;
		
		for (Element case_ : situation.getChildren()) {
			switch (case_.getName())
			{
				case Commons.CASE:
					 Case c = new Case();
					 c.Label = case_.getAttributeValue(Commons.LABEL);
					 c.Yield = case_.getAttributeValue(Commons.FORK);
					 for (Element item : case_.getChildren()) {
						 resolve(c, item);
					}
					 
					 si.Cases.add(c);
					 break;
			}
			
		}
		
		return si;
	}
	
	private void resolve (Case case_, Element element) throws XmlParsingException, UnregisteredOperatorException
	{
		switch (element.getName())
		{
		case Commons.EXPRESSION:
			if (currentState==2)
				throw new XmlParsingException("Nesting exception: a <expression> markup must be nested within a <operator> & a <situation> markup");
			case_.put(toExpression(element));
				currentState=2;
			break;
		case Commons.OPERATOR:
			case_.put(element.getAttributeValue(Commons.NAME));		
			currentState =1;
			for (Element e : element.getChildren()) {
				resolve(case_, e);
			} 
			break;
			
		 default:
			 	throw new XmlParsingException("Unrecognized xml node. Should not have come here...");
		}
		
	}
	
	private Expression toExpression (Element expression) throws XmlParsingException
	{
			
		switch (expression.getAttributeValue(Commons.TYPE))
		{
		case Commons.TYPE_QUANT:
			return new QuantifiableExpression(
					expression.getAttributeValue(Commons.LABEL),
					expression.getAttributeValue(Commons.RESULT)
					);
		case Commons.TYPE_DYNA:
			return new DynamicExpression(
					expression.getAttributeValue(Commons.LABEL),
					expression.getAttributeValue(Commons.RESULT)
					);
		case Commons.TYPE_LOGIC:
			return new LogicalExpression(
					expression.getAttributeValue(Commons.LABEL),
					expression.getAttributeValue(Commons.RESULT)
					);
		 default:
			throw new XmlParsingException("Unrecognized xml node. Should not have come here...");
		}
			
	}

	/**
	 * Build from the target XML file
	 *
	 * @return List
	 * @throws JDOMException
	 * @throws IOException
	 * @throws XmlParsingException
	 * @throws UnregisteredOperatorException
	 */
	public List<Scenario> build() throws JDOMException, IOException, XmlParsingException, UnregisteredOperatorException
	{
		List<Scenario> scenarios = new ArrayList<>();
		
	
		
		
		for (Element scenario : new SAXBuilder().build(new File(_xml)).getRootElement().getChildren(Commons.SCENARIO)) {
			Scenario sc = new Scenario();
			
			sc._name = scenario.getAttributeValue(Commons.NAME);
			
			for (Element situation : scenario.getChildren(Commons.SITUATION)) {
				sc.put(toSituation(situation));
			}
			
			scenarios.add(sc);
		}
		
		return scenarios;
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Callable#call()
	 */
	@Override
	public List<Scenario> call() throws Exception {
		List<Scenario> _scenarios = build();
		_callback.callback(this, _scenarios);
		return _scenarios;
		
	}

}
